package com.example.mypetstoreback.log;


import com.auth0.jwt.interfaces.DecodedJWT;
import com.example.mypetstoreback.pojo.logInfo;
import com.example.mypetstoreback.service.logService;
import com.example.mypetstoreback.util.jwtUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Method;
import java.util.Date;


//实现日志功能
@Aspect
@Component
public class SyslogConfig {
    @Autowired
    private logService logservice;

    private long methodDoStartTime;

    //设置一个切点，只要调用了@Syslog注解的方法都要进行日志记录
    @Pointcut("@annotation(com.example.mypetstoreback.log.Syslog)")
    public void sysLogPointCut(){}

    //指定在哪一个切点之前进行插入，前置通知：记录方法被调用的时间，方法执行时间计时器开始
    @Before("sysLogPointCut()")
    //获取切点point
    public void beforeMethod(JoinPoint point){
        this.methodDoStartTime = System.currentTimeMillis();
    }

    //通知-返回后通知-方法调用成功 returning这个参数指定的是对应连接点方法所返回的内容
    // JoinPoint表示连接点，可以通过这个连接点进行获取相关的信息
    @AfterReturning(value = "sysLogPointCut()",returning = "result")
    public void afterReturn(JoinPoint joinPoint,Object result){
        //获取当前方法结束返回的时间和开始进入方法的时间进行比较，得到方法的执行时间
        long doMethodTime = System.currentTimeMillis() - this.methodDoStartTime;

        //编写正常的日志记录
        logInfo loginfo = new logInfo();

        //在Spring容器中快速获取当前线程的请求对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) RequestContextHolder.getRequestAttributes().resolveReference(RequestAttributes.REFERENCE_REQUEST);
        try{
            loginfo.setInsertIP(getIPAddress(httpServletRequest)); // 获取客户端IP地址
            loginfo.setAccountName(getUserNameByToken(httpServletRequest)); // 当前请求操作的用户名（账户名）暂定优化后续使用JWT
            loginfo.setPath(httpServletRequest.getRequestURI()); // 请求后端地址( 如： /sys/login)
            loginfo.setGetTime(new Date(this.methodDoStartTime)); // 方法调用时间
            loginfo.setOverTime(new Date()); // 方法执行完成返回的时间
            loginfo.setDoTime(String.valueOf(doMethodTime)+"ms"); // 方法执行时间

            //获取aop拦截的方法 - class字节码对象
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();  //aop拦截的方法字节码对象
            String methodName = method.getName();   //java方法名，controller方法名称

            System.out.println(getParamStrByObjectArr(joinPoint.getArgs()));
            loginfo.setMethodParam(getParamStrByObjectArr(joinPoint.getArgs()));
            loginfo.setModelName(methodName);
            loginfo.setMethodName(methodName);

            //获取当前方法头部的@MySysLog注解,把每个日志所要记录的不同的详细信息放在注解中
            Syslog annotation = method.getAnnotation(Syslog.class);

            if (annotation != null){
                String modelName = annotation.modelName();
                String methodDesc = annotation.methodDesc();
                int methodType = annotation.methodType();
                int sysType = annotation.sysType();
                loginfo.setModelName(modelName);
                loginfo.setMethodDesc(methodDesc);
                loginfo.setMethodType(methodType);
                loginfo.setSysType(sysType);
            }

            loginfo.setResType(1);

            logservice.saveLog(loginfo);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //通知-异常后通知--错误日志记录
    @AfterThrowing(value = "sysLogPointCut()",throwing = "e")
    public void afterException(JoinPoint joinPoint,Throwable e){
        //获取当前方法结束返回的时间和开始进入方法的时间进行比较，得到方法的执行时间
        long doMethodTime = System.currentTimeMillis() - this.methodDoStartTime;

        //编写正常的日志记录
        logInfo loginfo = new logInfo();

        //在Spring容器中快速获取当前线程的请求对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) RequestContextHolder.getRequestAttributes().resolveReference(RequestAttributes.REFERENCE_REQUEST);
        try{
            loginfo.setInsertIP(getIPAddress(httpServletRequest)); // 获取客户端IP地址
            loginfo.setAccountName(getUserNameByToken(httpServletRequest)); // 当前请求操作的用户名（账户名）暂定优化后续使用JWT
            loginfo.setPath(httpServletRequest.getRequestURI()); // 请求后端地址( 如： /sys/login)
            loginfo.setGetTime(new Date(this.methodDoStartTime)); // 方法调用时间
            loginfo.setOverTime(new Date()); // 方法执行完成返回的时间
            loginfo.setDoTime(String.valueOf(doMethodTime)+"ms"); // 方法执行时间

            //获取aop拦截的方法 - class字节码对象
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();  //aop拦截的方法字节码对象
            String methodName = method.getName();   //java方法名，controller方法名称

            loginfo.setMethodParam(getParamStrByObjectArr(joinPoint.getArgs()));
            loginfo.setModelName(methodName);
            loginfo.setMethodName(methodName);

            //获取当前方法头部的@MySysLog注解,把每个日志所要记录的不同的详细信息放在注解中
            Syslog annotation = method.getAnnotation(Syslog.class);

            if (annotation != null){
                String modelName = annotation.modelName();
                String methodDesc = annotation.methodDesc();
                int methodType = annotation.methodType();
                int sysType = annotation.sysType();
                loginfo.setModelName(modelName);
                loginfo.setMethodDesc(methodDesc);
                loginfo.setMethodType(methodType);
                loginfo.setSysType(sysType);
            }

            loginfo.setResType(2);
            loginfo.setYcMsg(e.getMessage()+"||"+e.toString().substring(80)+"...");

            logservice.saveLog(loginfo);
        }catch (Exception e2){
            e2.printStackTrace();
        }
    }

    //获取客户端的IP地址
    public static String getIPAddress(HttpServletRequest request){
        //获取ip
        String ip = request.getHeader("x-forwarded-for");

        //当ip为unknown的时候，即当ip不属于x-forwarded-for类型的时候再不断地去判断然后想法设法去请求头里面获取ip地址
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        return ip;
    }

    public static String getParamStrByObjectArr(Object [] arr){
        String str = "";

        for (Object obj : arr){
            str += obj.toString()+",";
        }
        if (str.length() > 0)
            str = str.substring(0,str.length()-1);

        return str;
    }
    public static String getUserNameByToken(HttpServletRequest request){
        String token = request.getHeader("Authorization");
        if(token == null){
            return "";
        }

        // 获取token签名内容
        try{
            DecodedJWT verify = jwtUtils.verity(token);

            String userName = verify.getClaim("username").asString();
            System.out.println("userName"+userName);
            return userName;
        }catch (Exception e){
            return "";
        }

    }
}
